﻿<!--
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
-->

<UserControl x:Class="System.Windows.Controls.Samples.CheckedTreeViewItemSample"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:common="clr-namespace:System.Windows;assembly=System.Windows.Controls.Toolkit"
    xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
    xmlns:samplesCommon="clr-namespace:System.Windows.Controls.Samples;assembly=System.Windows.Controls.Samples.Common"
    >
    <StackPanel>

        <StackPanel.Resources>

            <!-- Sample features to install -->
            <toolkit:ObjectCollection x:Key="MediaPlayerFeatures">
                <samplesCommon:Feature FeatureName="Media Player" Description="Media Player for playback of music and videos.">
                    <samplesCommon:Feature FeatureName="Audio" Description="Audio-related components that enable playback of various audio formats.">
                        <samplesCommon:Feature FeatureName="MP3 Codec" Description="Enable playback of files in the MP3 format." />
                        <samplesCommon:Feature FeatureName="WMA Codec" Description="Enable playback of files in the WMA format." />
                    </samplesCommon:Feature>
                    <samplesCommon:Feature FeatureName="Video" Description="Video-related components that enable playback of various audio formats.">
                        <samplesCommon:Feature FeatureName="WMV Codec" Description="Enable playback of files in the WMV format." />
                        <samplesCommon:Feature FeatureName="AVI Codec" Description="Enable playback of files in the AVI format." />
                    </samplesCommon:Feature>
                </samplesCommon:Feature>
            </toolkit:ObjectCollection>

            <!-- Tempate representing a Feature item -->
            <common:HierarchicalDataTemplate x:Key="NodeTemplate"  ItemsSource="{Binding Subcomponents}">
                <StackPanel Orientation="Horizontal" ToolTipService.ToolTip="{Binding Description}">
                    <CheckBox
                        IsTabStop="False"                        
                        IsThreeState="{Binding HasSubcomponents}"
                        IsChecked="{Binding ShouldInstall, Mode=TwoWay}"
                        Click="ItemCheckbox_Click"
                        />

                    <ContentPresenter Content="{Binding FeatureName}" />
                </StackPanel>
            </common:HierarchicalDataTemplate>

        </StackPanel.Resources>

        <ContentControl Content="Using CheckBoxes in a TreeView" Style="{StaticResource Header}" />

        <TextBlock Style="{StaticResource Information}">
            This sample demonstrates using CheckBoxes in a TreeView to select 
            components and sub-components, similiar to what
            would be seen in an installation or configuration dialog.
            <LineBreak />
            <LineBreak />
            A custom class, Feature, is used as a model to represent which
            features should be installed via its ShouldInstall property which 
            is bound to CheckBox.IsChecked.  This allows easy access to the 
            checked state without excessively searching the visual tree.           
        </TextBlock>

        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="2*" />
            </Grid.ColumnDefinitions>

            <toolkit:TreeView
                Grid.Column="0"                
                ItemTemplate="{StaticResource NodeTemplate}"
                ItemsSource="{StaticResource MediaPlayerFeatures}">
                
                <!-- A simple style to start the tree expanded -->
                <toolkit:TreeView.ItemContainerStyle>                    
                    <Style TargetType="toolkit:TreeViewItem">
                        <Setter Property="IsExpanded" Value="True" />
                    </Style>
                </toolkit:TreeView.ItemContainerStyle>
                
            </toolkit:TreeView>

            <Border BorderBrush="Gray" BorderThickness="1" Padding="8" Margin="8,0,0,0" Grid.Column="1">                
                <TextBlock TextWrapping="Wrap">
                    Checking items in the tree causes the children and parent 
                    items to update accordingly, regardless of depth or
                    visibility.
                    <LineBreak />
                    <LineBreak />
                    A CheckBox with a dash means that only some of the 
                    components of the feature are selected.
                </TextBlock>
            </Border>
                        
        </Grid>

        <!-- Source -->
        <src:SourceViewer xmlns:src="clr-namespace:System.Windows.Controls.Samples;assembly=System.Windows.Controls.Samples.Common" xmlns:sys="clr-namespace:System;assembly=mscorlib">
  <src:SourceFile Path="CheckedTreeViewItemSample.xaml">
    <src:SourceFile.Source>
      <sys:String>&lt;!--
// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
--&gt;

&lt;UserControl x:Class="System.Windows.Controls.Samples.CheckedTreeViewItemSample"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:common="clr-namespace:System.Windows;assembly=System.Windows.Controls.Toolkit"
    xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
    xmlns:samplesCommon="clr-namespace:System.Windows.Controls.Samples;assembly=System.Windows.Controls.Samples.Common"
    &gt;
    &lt;StackPanel&gt;

        &lt;StackPanel.Resources&gt;

            &lt;!-- Sample features to install --&gt;
            &lt;toolkit:ObjectCollection x:Key="MediaPlayerFeatures"&gt;
                &lt;samplesCommon:Feature FeatureName="Media Player" Description="Media Player for playback of music and videos."&gt;
                    &lt;samplesCommon:Feature FeatureName="Audio" Description="Audio-related components that enable playback of various audio formats."&gt;
                        &lt;samplesCommon:Feature FeatureName="MP3 Codec" Description="Enable playback of files in the MP3 format." /&gt;
                        &lt;samplesCommon:Feature FeatureName="WMA Codec" Description="Enable playback of files in the WMA format." /&gt;
                    &lt;/samplesCommon:Feature&gt;
                    &lt;samplesCommon:Feature FeatureName="Video" Description="Video-related components that enable playback of various audio formats."&gt;
                        &lt;samplesCommon:Feature FeatureName="WMV Codec" Description="Enable playback of files in the WMV format." /&gt;
                        &lt;samplesCommon:Feature FeatureName="AVI Codec" Description="Enable playback of files in the AVI format." /&gt;
                    &lt;/samplesCommon:Feature&gt;
                &lt;/samplesCommon:Feature&gt;
            &lt;/toolkit:ObjectCollection&gt;

            &lt;!-- Tempate representing a Feature item --&gt;
            &lt;common:HierarchicalDataTemplate x:Key="NodeTemplate"  ItemsSource="{Binding Subcomponents}"&gt;
                &lt;StackPanel Orientation="Horizontal" ToolTipService.ToolTip="{Binding Description}"&gt;
                    &lt;CheckBox
                        IsTabStop="False"                        
                        IsThreeState="{Binding HasSubcomponents}"
                        IsChecked="{Binding ShouldInstall, Mode=TwoWay}"
                        Click="ItemCheckbox_Click"
                        /&gt;

                    &lt;ContentPresenter Content="{Binding FeatureName}" /&gt;
                &lt;/StackPanel&gt;
            &lt;/common:HierarchicalDataTemplate&gt;

        &lt;/StackPanel.Resources&gt;

        &lt;ContentControl Content="Using CheckBoxes in a TreeView" Style="{StaticResource Header}" /&gt;

        &lt;TextBlock Style="{StaticResource Information}"&gt;
            This sample demonstrates using CheckBoxes in a TreeView to select 
            components and sub-components, similiar to what
            would be seen in an installation or configuration dialog.
            &lt;LineBreak /&gt;
            &lt;LineBreak /&gt;
            A custom class, Feature, is used as a model to represent which
            features should be installed via its ShouldInstall property which 
            is bound to CheckBox.IsChecked.  This allows easy access to the 
            checked state without excessively searching the visual tree.           
        &lt;/TextBlock&gt;

        &lt;Grid&gt;
            &lt;Grid.ColumnDefinitions&gt;
                &lt;ColumnDefinition Width="*" /&gt;
                &lt;ColumnDefinition Width="2*" /&gt;
            &lt;/Grid.ColumnDefinitions&gt;

            &lt;toolkit:TreeView
                Grid.Column="0"                
                ItemTemplate="{StaticResource NodeTemplate}"
                ItemsSource="{StaticResource MediaPlayerFeatures}"&gt;
                
                &lt;!-- A simple style to start the tree expanded --&gt;
                &lt;toolkit:TreeView.ItemContainerStyle&gt;                    
                    &lt;Style TargetType="toolkit:TreeViewItem"&gt;
                        &lt;Setter Property="IsExpanded" Value="True" /&gt;
                    &lt;/Style&gt;
                &lt;/toolkit:TreeView.ItemContainerStyle&gt;
                
            &lt;/toolkit:TreeView&gt;

            &lt;Border BorderBrush="Gray" BorderThickness="1" Padding="8" Margin="8,0,0,0" Grid.Column="1"&gt;                
                &lt;TextBlock TextWrapping="Wrap"&gt;
                    Checking items in the tree causes the children and parent 
                    items to update accordingly, regardless of depth or
                    visibility.
                    &lt;LineBreak /&gt;
                    &lt;LineBreak /&gt;
                    A CheckBox with a dash means that only some of the 
                    components of the feature are selected.
                &lt;/TextBlock&gt;
            &lt;/Border&gt;
                        
        &lt;/Grid&gt;

        &lt;!-- Source --&gt;
    &lt;/StackPanel&gt;
&lt;/UserControl&gt;</sys:String>
    </src:SourceFile.Source>
  </src:SourceFile>
  <src:SourceFile Path="CheckedTreeViewItemSample.xaml.cs">
    <src:SourceFile.Source>
      <sys:String>// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.ComponentModel;

namespace System.Windows.Controls.Samples
{
    /// &lt;summary&gt;
    /// Sample demonstrating CheckBoxes in a TreeView.
    /// &lt;/summary&gt;    
    [Sample("(2)Using CheckBoxes", DifficultyLevel.Scenario)]  
    [Category("TreeView")]
    public partial class CheckedTreeViewItemSample : UserControl
    {
        /// &lt;summary&gt;
        /// Initializes a new instance of the CheckedTreeViewSample class.
        /// &lt;/summary&gt;
        public CheckedTreeViewItemSample()
        {
            InitializeComponent();
        }

        /// &lt;summary&gt;
        /// Handle the ItemCheckbox.Click event.
        /// &lt;/summary&gt;
        /// &lt;param name="sender"&gt;The CheckBox.&lt;/param&gt;
        /// &lt;param name="e"&gt;Event arguments.&lt;/param&gt;
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from an event declared in XAML")]
        private void ItemCheckbox_Click(object sender, RoutedEventArgs e)
        {
            TreeViewItem item = GetParentTreeViewItem((DependencyObject)sender);
            if (item != null)
            {
                Feature feature = item.DataContext as Feature;
                if (feature != null)
                {
                    UpdateChildrenCheckedState(feature);
                    UpdateParentCheckedState(item);
                }
            }
        }

        /// &lt;summary&gt;
        /// Gets the parent TreeViewItem of the passed in dependancy object.
        /// &lt;/summary&gt;
        /// &lt;param name="item"&gt;Item whose parent to wish to find.&lt;/param&gt;
        /// &lt;returns&gt;
        /// If item is a TreeViewItem then returns its parent TreeViewItem,
        /// else returns the TreeViewItem containing the item.
        /// &lt;/returns&gt;
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from an event declared in XAML")]
        private static TreeViewItem GetParentTreeViewItem(DependencyObject item)
        {
            if (item != null)
            {
                DependencyObject parent = VisualTreeHelper.GetParent(item);
                TreeViewItem parentTreeViewItem = parent as TreeViewItem;
                return (parentTreeViewItem != null) ? parentTreeViewItem : GetParentTreeViewItem(parent);
            }
            return null;
        }

        /// &lt;summary&gt;
        /// Sets the Feature bound to the item's parent to the combined
        /// check state of all the children.
        /// &lt;/summary&gt;
        /// &lt;param name="item"&gt;Item whose parent should be adjust.&lt;/param&gt;
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from an event declared in XAML")]
        private static void UpdateParentCheckedState(TreeViewItem item)
        {
            TreeViewItem parent = GetParentTreeViewItem(item);
            if (parent != null)
            {
                Feature feature = parent.DataContext as Feature;
                if (feature != null)
                {
                    // Get the combined checked state of all the children,
                    // determing if they're all checked, all unchecked or a
                    // combination.
                    bool? childrenCheckedState = feature.Subcomponents.First&lt;Feature&gt;().ShouldInstall;
                    for (int i = 1; i &lt; feature.Subcomponents.Count(); i++)
                    {
                        if (childrenCheckedState != feature.Subcomponents[i].ShouldInstall)
                        {
                            childrenCheckedState = null;
                            break;
                        }
                    }

                    // Set the parent to the combined state of the children.
                    feature.ShouldInstall = childrenCheckedState;

                    // Continue up the tree updating each parent with the
                    // correct combined state.
                    UpdateParentCheckedState(parent);
                }
            }
        }

        /// &lt;summary&gt;
        /// Sets the feature's children checked states, including subcomponents,
        /// to match the state of feature.
        /// &lt;/summary&gt;
        /// &lt;param name="feature"&gt;Feature whose children should be set.&lt;/param&gt;
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from an event declared in XAML")]
        private static void UpdateChildrenCheckedState(Feature feature)
        {
            if (feature.ShouldInstall.HasValue)
            {
                foreach (Feature childFeature in feature.Subcomponents)
                {
                    childFeature.ShouldInstall = feature.ShouldInstall;
                    if (childFeature.Subcomponents.Count() &gt; 0)
                    {
                        UpdateChildrenCheckedState(childFeature);
                    }
                }
            }
        }
    }
}</sys:String>
    </src:SourceFile.Source>
  </src:SourceFile>
  <src:SourceFile Path="CheckedTreeViewItemSample.xaml.vb">
    <src:SourceFile.Source>
      <sys:String>' (c) Copyright Microsoft Corporation.
' This source is subject to the Microsoft Public License (Ms-PL).
' Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
' All other rights reserved.

Imports Microsoft.VisualBasic
Imports System.Diagnostics.CodeAnalysis
Imports System.Linq
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Media
Imports System.ComponentModel

''' &lt;summary&gt;
''' Sample demonstrating CheckBoxes in a TreeView.
''' &lt;/summary&gt;    
&lt;Sample("(2)Using CheckBoxes", DifficultyLevel.Scenario), Category("TreeView")&gt; _
Partial Public Class CheckedTreeViewItemSample
    Inherits UserControl
    ''' &lt;summary&gt;
    ''' Initializes a new instance of the CheckedTreeViewSample class.
    ''' &lt;/summary&gt;
    Public Sub New()
        InitializeComponent()
    End Sub

    ''' &lt;summary&gt;
    ''' Handle the ItemCheckbox.Click event.
    ''' &lt;/summary&gt;
    ''' &lt;param name="sender"&gt;The CheckBox.&lt;/param&gt;
    ''' &lt;param name="e"&gt;Event arguments.&lt;/param&gt;
    &lt;SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification:="Called from an event declared in XAML")&gt; _
    Private Sub ItemCheckbox_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        Dim item As TreeViewItem = GetParentTreeViewItem(CType(sender, DependencyObject))
        If item IsNot Nothing Then
            Dim feature As Feature = TryCast(item.DataContext, Feature)
            If feature IsNot Nothing Then
                UpdateChildrenCheckedState(feature)
                UpdateParentCheckedState(item)
            End If
        End If
    End Sub

    ''' &lt;summary&gt;
    ''' Gets the parent TreeViewItem of the passed in dependancy object.
    ''' &lt;/summary&gt;
    ''' &lt;param name="item"&gt;Item whose parent to wish to find.&lt;/param&gt;
    ''' &lt;returns&gt;
    ''' If item is a TreeViewItem then returns its parent TreeViewItem,
    ''' else returns the TreeViewItem containing the item.
    ''' &lt;/returns&gt;
    &lt;SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification:="Called from an event declared in XAML")&gt; _
    Private Shared Function GetParentTreeViewItem(ByVal item As DependencyObject) As TreeViewItem
        If item IsNot Nothing Then
            Dim parent As DependencyObject = VisualTreeHelper.GetParent(item)
            Dim parentTreeViewItem As TreeViewItem = TryCast(parent, TreeViewItem)
            Return If((parentTreeViewItem IsNot Nothing), parentTreeViewItem, GetParentTreeViewItem(parent))
        End If
        Return Nothing
    End Function

    ''' &lt;summary&gt;
    ''' Sets the Feature bound to the item's parent to the combined
    ''' check state of all the children.
    ''' &lt;/summary&gt;
    ''' &lt;param name="item"&gt;Item whose parent should be adjust.&lt;/param&gt;
    &lt;SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification:="Called from an event declared in XAML")&gt; _
    Private Shared Sub UpdateParentCheckedState(ByVal item As TreeViewItem)
        Dim parent As TreeViewItem = GetParentTreeViewItem(item)
        If parent IsNot Nothing Then
            Dim feature As Feature = TryCast(parent.DataContext, Feature)
            If feature IsNot Nothing Then
                ' Get the combined checked state of all the children,
                ' determing if they're all checked, all unchecked or a
                ' combination.
                Dim childrenCheckedState? As Boolean = feature.Subcomponents.First().ShouldInstall
                For i As Integer = 1 To feature.Subcomponents.Count() - 1
                    If Not childrenCheckedState.Equals(feature.Subcomponents(i).ShouldInstall) Then
                        childrenCheckedState = Nothing
                        Exit For
                    End If
                Next i

                ' Set the parent to the combined state of the children.
                feature.ShouldInstall = childrenCheckedState

                ' Continue up the tree updating each parent with the
                ' correct combined state.
                UpdateParentCheckedState(parent)
            End If
        End If
    End Sub

    ''' &lt;summary&gt;
    ''' Sets the feature's children checked states, including subcomponents,
    ''' to match the state of feature.
    ''' &lt;/summary&gt;
    ''' &lt;param name="feature"&gt;Feature whose children should be set.&lt;/param&gt;
    &lt;SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification:="Called from an event declared in XAML")&gt; _
    Private Shared Sub UpdateChildrenCheckedState(ByVal feature As Feature)
        If feature.ShouldInstall.HasValue Then
            For Each childFeature As Feature In feature.Subcomponents
                childFeature.ShouldInstall = feature.ShouldInstall
                If childFeature.Subcomponents.Count() &gt; 0 Then
                    UpdateChildrenCheckedState(childFeature)
                End If
            Next childFeature
        End If
    End Sub
End Class
</sys:String>
    </src:SourceFile.Source>
  </src:SourceFile>
</src:SourceViewer>
    </StackPanel>
</UserControl>